package adapter

import (
	"encoding/json"
	"errors"
	"fmt"
	"migration-tool/adapter/aone"
	"migration-tool/common"
	"migration-tool/target"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	AoneErrorCodePermissionDenied  = "SYSTEM_FORBIDDEN_ERROR"
	AoneErrorCodeUnAuthorizedError = "SYSTEM_UNAUTHORIZED_ERROR"
	AoneErrorCodeUserNotFound      = "USER_NOT_FOUND_ERROR"
)

const (
	AoneOpenApiGetAllAuthorizedRepo = "https://code.alibaba-inc.com/api/v3/projects?per_page={{.PageSize}}&sort=desc&v2=true&page="
	AoneOpenApiGetOneRepoInfo       = "https://code.alibaba-inc.com/api/v3/project?path={{.RepoName}}"
	AoneOpenApiGetUserEmail         = "https://code.alibaba-inc.com/api/v3/user/info?extern_uid={{.ExternUid}}"
	AoneOpenApiGetAllGroups         = "https://code.alibaba-inc.com/api/v3/groups?per_page={{.PageSize}}&page="
	AoneOpenApiGetGroupMembers      = "https://code.alibaba-inc.com/api/v3/groups/{{.DataId}}/members?per_page={{.PageSize}}&page="

	//AoneOpenApiGetAllRepoIssues        = "https://code.alibaba-inc.com/api/v4/projects/{{.RepoId}}/issues/list?v2=true"
	AoneOpenApiGetAllMergeRequests              = "https://code.alibaba-inc.com/api/v3/projects/{{.RepoId}}/merge_requests?&order_by=updated_at&sort=desc&per_page={{.PageSize}}&page="
	AoneOpenApiGetAllCommentsForOneMergeRequest = "https://code.alibaba-inc.com/api/v3/projects/{{.RepoId}}/merge_request/{{.DataId}}/comments?per_page={{.PageSize}}&page="
	AoneOpenApiGetRepoMembers                   = "https://code.alibaba-inc.com/api/v3/projects/{{.RepoId}}/members?per_page={{.PageSize}}&page="
	AoneOpenApiGetRepoMetaInfo                  = "https://code.alibaba-inc.com/api/v3/projects/{{.RepoId}}"
	AoneOpenApiGetProtectBranchSetting          = "https://code.alibaba-inc.com/api/v4/projects/{{.RepoId}}/repository/branches/protect"
	AoneOpenApiGetCodeReviewSetting             = "https://code.alibaba-inc.com/api/v4/projects/{{.RepoId}}/code_review/settings"
	AoneOpenApiGetPushRuleSetting               = "https://code.alibaba-inc.com/api/v4/projects/{{.RepoId}}/push_rule"
	AoneOpenApiGetWebHookSetting                = "https://code.alibaba-inc.com/api/v3/projects/{{.RepoId}}/hooks?per_page={{.PageSize}}&page="
	AoneOpenApiGetDeployKeysSetting             = "https://code.alibaba-inc.com/api/v3/projects/{{.RepoId}}/keys/get_projects?per_page={{.PageSize}}&page="
)

type aoneUserDetailArgs struct {
	Token string
	User  *target.CodeupUser
}

func aoneGetAllRepoInfo(cfg *common.Config) (RepoInfoArray, error) {
	if cfg.BaseConfig.Export {
		return aoneGetAllRepoInfoForExport(cfg)
	} else {
		return aoneGetAllRepoInfoForImport(cfg)
	}
}

// aoneGetAllRepoInfoForImport
// 导入时使用，获取所有已导出的仓库
// 这里的结果不经过Config中的repo和excludeRepo的过滤
func aoneGetAllRepoInfoForImport(cfg *common.Config) (RepoInfoArray, error) {
	tmplArgs := TmplArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: AonePlatformName,
		FileName:           JsonFileNameAllRepoInfo,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		StringTmpl:         ExportEnterpriseFileTmpl,
	}

	filePath, err := FillParamIntoStringTmpl(tmplArgs)

	var repoInfo RepoInfoArray
	_, err = os.Stat(filePath)
	if err == nil {
		data, err := os.ReadFile(filePath)
		if err != nil {
			return nil, err
		}
		err = json.Unmarshal(data, &repoInfo)
		if err != nil {
			return nil, err
		}
	} else if os.IsNotExist(err) {
		panic("请先进行一次导出工作，以便生成repo-info.json文件供导入使用")
	}

	return repoInfo, nil
}

// aoneGetAllRepoInfoForExport
// 导出时使用，获取所有Token已授权仓库
// 这里的结果不经过Config中的repo和excludeRepo的过滤
// 获得的仓库需要持久化
func aoneGetAllRepoInfoForExport(cfg *common.Config) (RepoInfoArray, error) {
	dataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		FileName:           JsonFileNameAllRepoInfo,
		ExportPlatformName: AonePlatformName,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
	}

	// filePath为之后保存仓库信息文件的文件路径
	filePath, err := MkdirFromStringTmpl(dataArgs)
	if cfg.BaseConfig.UseProgress {
		// 代表使用Cache加速，但是Cache加速只在一个时间端有效
		stat, err := os.Stat(filePath)
		if err == nil {
			interval := time.Now().Sub(stat.ModTime())
			// 在时间间隔内，就进行读缓存加速
			if interval.Seconds() < RepoInfoCacheIntervalSeconds {
				var repoInfo RepoInfoArray
				data, err := os.ReadFile(filePath)
				if err != nil {
					return nil, err
				}
				err = json.Unmarshal(data, &repoInfo)
				if err != nil {
					return nil, err
				}
				// 返回所有获取的仓库
				return repoInfo, nil
			}
		}
	}

	tmplArgs := TmplArgs{
		PageSize:   cfg.BaseConfig.PageSize,
		StringTmpl: AoneOpenApiGetAllAuthorizedRepo,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return nil, err
	}

	page := 1
	workerNum := 0
	var requestError error
	requestError = nil
	repoMap := sync.Map{}
	onceQuery := func(queryPage int, baseQueryUrl string) error {
		realUrl := baseQueryUrl + strconv.Itoa(queryPage)

		var data aone.RepoMetaInfoList
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              cfg.BaseConfig.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			return nil
		}

		for _, val := range data.Data {
			var repo RepoInfo
			repo.RepoId = val.ID
			repo.RepoGroupName = val.Namespace.Name
			repo.RepoName = val.Name
			repo.CodeCloneHttpUrl = val.HTTPURLToRepo
			repo.WikiEnable = val.WikiEnabled
			repoMap.Store(val.ID, &repo)
		}
		return nil
	}

	startQuery := func(baseQueryUrl string, wg *sync.WaitGroup) {
		for idx := 0; idx < workerNum; idx++ {
			queryPage := page
			page += 1
			wg.Add(1)
			go func(queryPage int) {
				//cfg.GlobalLog.Infof("执行一次 页号为：%d", queryPage)
				err := onceQuery(queryPage, baseQueryUrl)
				if err != nil {
					requestError = err
				}
				wg.Done()
			}(queryPage)
		}
	}

	realUrl := baseUrl + strconv.Itoa(1)

	var getTotalCount aone.RepoMetaInfoList
	requestArgs := RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            realUrl,
		Token:              cfg.BaseConfig.Token,
		RetVal:             &getTotalCount,
	}
	if err := DoGetRequest(requestArgs); err != nil {
		return nil, err
	}
	workerNum = int(getTotalCount.Amount)/cfg.BaseConfig.PageSize + 1

	page = 1
	waitGroup := &sync.WaitGroup{}
	startQuery(baseUrl, waitGroup)
	waitGroup.Wait()
	if requestError != nil {
		return nil, requestError
	}

	// 添加进map中，因为repo信息不可能改变，所以不需要判定是否已存在
	// 只要做到去重即可
	var authorizedRepos RepoInfoArray
	var rangeError error
	rangeError = nil

	repoMap.Range(func(key, val any) bool {
		v, ok := val.(*RepoInfo)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		authorizedRepos = append(authorizedRepos, *v)
		return true
	})

	if rangeError != nil {
		return nil, rangeError
	}

	// 将所有已导出仓库持久化
	data, err := json.Marshal(authorizedRepos)
	if err != nil {
		return nil, err
	}
	err = os.WriteFile(filePath, data, os.ModePerm)
	if err != nil {
		return nil, err
	}

	return authorizedRepos, nil
}

func aoneDealGroup(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}

	tmplArgs := TmplArgs{
		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetAllGroups,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}
	// 获取Group元信息
	var groupList target.CodeupNamespaceList
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []aone.Group
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}

		for _, val := range data {
			var namespace target.CodeupNamespace

			namespace.Id = val.ID
			namespace.Name = val.Name
			namespace.Description = val.Description
			namespace.ParentId = val.ParentID
			namespace.VisibilityLevel = val.VisibilityLevel
			namespace.Creator = &target.CodeupUser{
				Id: val.OwnerID,
			}
			groupList.NamespaceList = append(groupList.NamespaceList, &namespace)
		}
	}

	// Group元信息落盘
	if len(groupList.NamespaceList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameGroups,
			ExportPlatformName: AonePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			Data:               &groupList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(groupList.NamespaceList)) + "条Group信息数据"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func aoneDealGroupMember(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}

	tmplArgs := TmplArgs{
		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetAllGroups,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}
	// 获取Group元信息
	var groupList target.CodeupNamespaceList
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []aone.Group
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}

		for _, val := range data {
			var namespace target.CodeupNamespace

			namespace.Id = val.ID
			namespace.Name = val.Name
			namespace.Description = val.Description
			namespace.ParentId = val.ParentID
			namespace.VisibilityLevel = val.VisibilityLevel
			namespace.Creator = &target.CodeupUser{
				Id: val.OwnerID,
			}
			groupList.NamespaceList = append(groupList.NamespaceList, &namespace)
		}
	}

	userDetailMap := make(map[int64]*target.CodeupUser)

	// 根据上述获得的Group元信息，获取Group成员信息
	var memberList target.CodeupMemberList
	for _, group := range groupList.NamespaceList {
		groupId := strconv.FormatInt(group.Id, 10)
		tmplArgs := TmplArgs{
			PageSize:   args.PageSize,
			DataId:     groupId,
			StringTmpl: AoneOpenApiGetGroupMembers,
		}
		baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
		if err != nil {
			return err
		}
		page = 1

		for {
			realUrl := baseUrl + strconv.Itoa(page)
			page += 1
			var data []aone.Member
			requestArgs := RequestArgs{
				ExportPlatformName: AonePlatformName,
				RealUrl:            realUrl,
				Token:              args.Token,
				RetVal:             &data,
			}
			if err = DoGetRequest(requestArgs); err != nil {
				return err
			}

			if len(data) == 0 {
				break
			}

			for _, val := range data {
				var groupMember target.CodeupMember

				if user, ok := userDetailMap[val.ID]; ok {
					groupMember.User = user
				} else {
					realUser := &target.CodeupUser{
						Id:       val.ID,
						ExternId: val.ExternUID,
						UserName: val.Username,
						Name:     val.Name,
						Email:    val.Email,
					}
					userDetailArgs := aoneUserDetailArgs{
						Token: args.Token,
						User:  realUser,
					}
					err := aoneGetUserDetail(userDetailArgs)
					if err != nil {
						return err
					}
					groupMember.User = realUser
					userDetailMap[val.ID] = realUser
				}
				groupMember.AccessLevel = val.AccessLevel
				groupMember.SourceId = group.Id
				memberList.RepoMember = append(memberList.RepoMember, &groupMember)
			}
		}
	}

	// Group成员信息落盘
	if len(memberList.RepoMember) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameGroupMembers,
			ExportPlatformName: AonePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			Data:               &memberList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(memberList.RepoMember)) + "条Group成员信息数据"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// aoneDealDeployKeySetting
// 导出部署密钥，API要求分页
func aoneDealDeployKeySetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetDeployKeysSetting,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var deployKeysList target.CodeupDeployKeysSettingList
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []aone.DeployKey
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}

		for _, val := range data {
			var projects []*target.CodeupDeployProject
			for _, p := range val.Projects {
				var project target.CodeupDeployProject
				project.Id = p.ID
				project.Name = p.Name
				project.PathWithNamespace = p.PathWithNamespace
				project.Path = p.Path
				project.NameWithNamespace = p.NameWithNamespace
				projects = append(projects, &project)
			}

			var deployKey target.CodeupDeployKeysSetting
			deployKey.Id = val.ID
			deployKey.PublicDeployKey = val.PublicDeployKey
			deployKey.Title = val.Title
			deployKey.FingerPrint = val.Fingerprint64
			deployKey.Projects = projects
			deployKeysList.DeployKeysList = append(deployKeysList.DeployKeysList, &deployKey)
		}
	}

	if len(deployKeysList.DeployKeysList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameDeployKeysSetting,
			ExportPlatformName: AonePlatformName,
			RepoInfo:           args.RepoInfo,
			FilePathTmpl:       ExportDataFileTmpl,
			Data:               &deployKeysList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(deployKeysList.DeployKeysList)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func aoneDealWebHookSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetWebHookSetting,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var webHookList target.CodeupWebHookSettingList
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []aone.WebHook
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}
		for _, val := range data {
			var webHook target.CodeupWebHookSetting
			webHook.Id = val.ID
			webHook.Url = val.URL
			webHook.ProjectId = val.ProjectID
			webHook.PushEvents = val.PushEvents
			webHook.EnableSslVerification = val.EnableSslVerification
			webHook.MergeRequestsEvents = val.MergeRequestsEvents
			webHook.IssueEvents = val.IssuesEvents
			webHook.NoteEvents = val.NoteEvents
			webHook.TagPushEvents = val.TagPushEvents
			webHookList.WebHookList = append(webHookList.WebHookList, &webHook)
		}
	}

	if len(webHookList.WebHookList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameWebHookSetting,
			ExportPlatformName: AonePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &webHookList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(webHookList.WebHookList)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func aoneDealPushRuleSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		StringTmpl: AoneOpenApiGetPushRuleSetting,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var list []aone.PushRule
	requestArgs := RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            baseUrl,
		Token:              args.Token,
		RetVal:             &list,
	}
	if err = DoGetRequest(requestArgs); err != nil {
		return err
	}

	var dataList target.CodeupPushRuleSettingList
	for _, val := range list {
		var setting target.CodeupPushRuleSetting
		setting.Id = val.ID
		setting.Name = val.Name
		setting.RuleInfo = val.RuleInfo
		setting.IsEnable = val.IsEnabled
		setting.UpdateBy = &target.CodeupUser{
			Id:       val.UpdatedBy.ID,
			ExternId: val.UpdatedBy.ExternUID,
			UserName: val.UpdatedBy.Username,
			Name:     val.UpdatedBy.Name,
			Email:    val.UpdatedBy.Email,
		}
		dataList.PushRuleSettingList = append(dataList.PushRuleSettingList, &setting)
	}

	realUrl := baseUrl + "/project"
	var data []aone.ProjectPushRule
	requestArgs = RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            realUrl,
		Token:              args.Token,
		RetVal:             &data,
	}
	if err = DoGetRequest(requestArgs); err != nil {
		return err
	}
	for _, val := range data {
		var setting target.CodeupProjectPushRuleSetting
		setting.CheckerName = val.CheckerName
		setting.IsOpened = val.IsOpened
		setting.ExtraMessage = val.ExtraMessage
		dataList.ProjectPushRuleSettingList = append(dataList.ProjectPushRuleSettingList, &setting)
	}

	if len(dataList.PushRuleSettingList) > 0 ||
		len(dataList.ProjectPushRuleSettingList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNamePushRuleSetting,
			ExportPlatformName: AonePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &dataList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(dataList.PushRuleSettingList)+len(dataList.ProjectPushRuleSettingList)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func aoneDealCodeReviewSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		StringTmpl: AoneOpenApiGetCodeReviewSetting,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var data aone.CodeReviewSetting
	requestArgs := RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            realUrl,
		Token:              args.Token,
		RetVal:             &data,
	}
	if err = DoGetRequest(requestArgs); err != nil {
		return err
	}

	var setting target.CodeupCodeReviewSetting

	setting.AllowSelfApproval = data.AllowSelfApproval
	setting.EmailGroups = data.EmailGroups
	setting.MergeTypes = data.MergeTypes
	setting.MinimunApproval = data.MinimunApproval
	setting.RequireP3CCheck = data.RequireP3CCheck

	var reviewerList []*target.CodeupUser
	for _, val := range data.DefaultReviewers {
		reviewer := &target.CodeupUser{
			Id:       val.ID,
			ExternId: val.ExternUID,
			UserName: val.Username,
			Name:     val.Name,
			Email:    val.Email,
		}
		reviewerList = append(reviewerList, reviewer)
	}
	setting.DefaultReviews = reviewerList

	var subscriberList []*target.CodeupUser
	for _, val := range data.DefaultSubscribers {
		subscriber := &target.CodeupUser{
			Id:       val.ID,
			ExternId: val.ExternUID,
			UserName: val.Username,
			Name:     val.Name,
			Email:    val.Email,
		}
		subscriberList = append(subscriberList, subscriber)
	}
	setting.DefaultSubscribers = subscriberList

	writeDataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           BinFileNameCodeReviewSetting,
		ExportPlatformName: AonePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               &setting,
	}

	_, err = WriteProtoData(writeDataArgs)
	if err != nil {
		return err
	}
	recordArgs.Msg = "数据落盘成功"
	recordTask(recordArgs)

	return nil
}

func aoneDealProtectBranchSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		StringTmpl: AoneOpenApiGetProtectBranchSetting,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var data []aone.ProtectBranch
	requestArgs := RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            realUrl,
		Token:              args.Token,
		RetVal:             &data,
	}
	if err = DoGetRequest(requestArgs); err != nil {
		return err
	}

	var dataList target.CodeupProtectBranchSettingList
	for _, val := range data {
		var setting target.CodeupProtectBranchSetting

		setting.WhoCanPush = val.WhoCanPush
		setting.AllowForcePush = val.AllowForcePush
		setting.WhoCanMerge = val.WhoCanMerge
		setting.DevelopersCanAccept = val.DevelopersCanAccept
		setting.DevelopersCanMerge = val.DevelopersCanMerge
		setting.DevelopersCanPush = val.DevelopersCanPush
		setting.Branch = val.Name
		setting.ProjectId = args.RepoInfo.RepoId
		dataList.ProtectBranchSettingList = append(dataList.ProtectBranchSettingList, &setting)
	}

	if len(dataList.ProtectBranchSettingList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameProtectBranchSetting,
			ExportPlatformName: AonePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &dataList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(dataList.ProtectBranchSettingList)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func aoneDealRepoMetaInfo(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		StringTmpl: AoneOpenApiGetRepoMetaInfo,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var data aone.RepoMetaInfo
	requestArgs := RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            realUrl,
		Token:              args.Token,
		RetVal:             &data,
	}
	if err = DoGetRequest(requestArgs); err != nil {
		return err
	}

	var metaInfo target.CodeupRepoMetaInfo
	var namespace target.CodeupNamespace

	namespace.Id = data.Namespace.ID
	namespace.Name = data.Namespace.Name
	namespace.Description = data.Namespace.Description
	namespaceCreator := &target.CodeupUser{
		Id:       data.Namespace.OwnerID,
		ExternId: "",
		UserName: "",
		Name:     "",
		Email:    "",
	}
	namespace.Creator = namespaceCreator
	namespace.ParentId = data.Namespace.ParentID
	namespace.Public = data.Namespace.Public
	namespace.VisibilityLevel = data.Namespace.VisibilityLevel

	metaInfo.Id = data.ID
	metaInfo.Name = data.Name
	metaInfo.CreateAt = data.CreatedAt.Unix()
	metaInfo.UpdateAt = data.LastActivityAt.Unix()
	metaInfo.PathWithNamespace = data.PathWithNamespace
	metaInfo.Description = data.Description
	metaInfoCreator := &target.CodeupUser{
		Id:       data.CreatorID,
		ExternId: "",
		UserName: "",
		Name:     "",
		Email:    "",
	}
	metaInfo.Creator = metaInfoCreator
	metaInfo.Public = data.Public
	metaInfo.SshUrlToRepo = data.SSHURLToRepo
	metaInfo.OrganizationId = "noOrganization"
	metaInfo.VisibilityLevel = data.VisibilityLevel
	metaInfo.LastActivityAt = data.LastActivityAt.Unix()
	metaInfo.NameWithNamespace = data.NameWithNamespace
	metaInfo.Namespace = &namespace

	writeDataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           BinFileNameRepoMetaInfo,
		ExportPlatformName: AonePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               &metaInfo,
	}

	_, err = WriteProtoData(writeDataArgs)
	if err != nil {
		return err
	}
	recordArgs.Msg = "数据落盘成功"
	recordTask(recordArgs)

	return nil
}

func aoneDealRepoMember(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetRepoMembers,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var memberList target.CodeupMemberList
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []aone.Member
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}

		for _, val := range data {
			var member target.CodeupMember
			member.AccessLevel = val.AccessLevel

			var user target.CodeupUser
			user.Id = val.ID
			user.Name = val.Name
			user.UserName = val.Username
			user.ExternId = val.ExternUID
			user.Email = val.Email

			member.User = &user
			member.SourceId = args.RepoInfo.RepoId
			memberList.RepoMember = append(memberList.RepoMember, &member)
		}
	}

	if len(memberList.RepoMember) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameRepoMembers,
			ExportPlatformName: AonePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &memberList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(memberList.RepoMember)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func aoneDealCode(args TaskArgs) (err error) {
	// 创建仓库clone对应的dir
	dataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           CloneCodeFileName,
		ExportPlatformName: AonePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               nil, // 不写数据，只创建dir
	}
	dir, err := MkdirFromStringTmpl(dataArgs)
	if err != nil {
		return err
	}
	lastIdx := strings.LastIndex(args.RepoInfo.CodeCloneHttpUrl, "/")
	filePath := dir + args.RepoInfo.CodeCloneHttpUrl[lastIdx+1:]

	var cmd *exec.Cmd
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		cmd = exec.Command("git", "-C", dir, "clone", "--mirror", args.RepoInfo.CodeCloneHttpUrl)
	} else if err == nil {
		cmd = exec.Command("git", "-C", filePath, "fetch", args.RepoInfo.CodeCloneHttpUrl, "+refs/*:refs/*")
	} else {
		return err
	}
	if err = cmd.Run(); err != nil {
		return err
	}

	return
}

func aoneDealWiki(args TaskArgs) (err error) {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	if !args.RepoInfo.WikiEnable {
		recordArgs.Msg = LogMsgTaskFailed + ":仓库并未开启wiki"
		recordTask(recordArgs)
		return nil
	}
	// 创建对应目录
	dataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           CloneWikiFileName,
		ExportPlatformName: AonePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               nil, // 不写数据，只创建dir
	}
	dir, err := MkdirFromStringTmpl(dataArgs)
	if err != nil {
		return err
	}
	lastDot := strings.LastIndex(args.RepoInfo.CodeCloneHttpUrl, ".")
	wikiUrl := args.RepoInfo.CodeCloneHttpUrl[:lastDot] + ".wiki.git"
	lastIdx := strings.LastIndex(wikiUrl, "/")
	filePath := dir + wikiUrl[lastIdx+1:]
	var cmd *exec.Cmd
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		cmd = exec.Command("git", "-C", dir, "clone", "--mirror", wikiUrl)
	} else if err == nil {
		cmd = exec.Command("git", "-C", filePath, "fetch", wikiUrl, "+refs/*:refs/*")
	} else {
		return err
	}
	if err = cmd.Run(); err != nil {
		return err
	}
	return
}

func aoneGetUserDetail(args aoneUserDetailArgs) error {
	if args.User.Email == "" && args.User.ExternId != "" {
		tmplArgs := TmplArgs{
			ExternUid:  args.User.ExternId,
			StringTmpl: AoneOpenApiGetUserEmail,
		}
		realUrl, err := FillParamIntoStringTmpl(tmplArgs)
		if err != nil {
			return err
		}
		var userEmail aone.UserEmail
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &userEmail,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}
		args.User.Email = userEmail.Email
	}
	return nil
}

func aoneWorkerFuncDealMrCmt(args *WorkerFuncArgs) error {
	tmplArgs := TmplArgs{
		RepoId: args.RepoInfo.RepoId,
		DataId: args.DataId,

		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetAllCommentsForOneMergeRequest,
	}

	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	finalDataList, ok := args.Data.(*target.CodeupMergeRequestList)
	if !ok {
		args.Log.Error("Mr数据传递错误")
		return errors.New("mr数据传递错误")
	}
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []aone.Comment
		requestArgs := RequestArgs{
			ExportPlatformName: AonePlatformName,
			RealUrl:            realUrl,
			Token:              args.Token,
			RetVal:             &data,
		}
		if err = DoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}
		//args.Log.Info("MergeRequestId：", args.MrList.MergeRequestList[mrIdx].Id, "获取长度：", len(data), "url", realUrl)
		for _, val := range data {
			var cmt target.CodeupMergeRequestComment
			cmt.Author = &target.CodeupUser{
				Id:       val.Author.ID,
				ExternId: val.Author.ExternUID,
				UserName: val.Author.Username,
				Name:     val.Author.Name,
				Email:    val.Author.Email,
			}

			oneUser := &target.CodeupUser{
				Id:       val.Author.ID,
				ExternId: val.Author.ExternUID,
				UserName: val.Author.Username,
				Name:     val.Author.Name,
				Email:    val.Author.Email,
			}
			args.UserMap.Store(val.Author.ID, oneUser)

			cmt.Id = val.ID
			cmt.ProjectId = val.ProjectID
			cmt.Note = val.Note
			cmt.System = val.System
			//cmt.CreatedAt = timestamppb.New(val.CreatedAt)
			//cmt.UpdatedAt = timestamppb.New(val.UpdatedAt)
			cmt.CreateAt = val.CreatedAt.Unix()
			cmt.UpdateAt = val.UpdatedAt.Unix()
			cmt.MergeRequestId = finalDataList.MergeRequestList[args.FinalDataIndex].Id
			finalDataList.MergeRequestList[args.FinalDataIndex].CommentList = append(finalDataList.MergeRequestList[args.FinalDataIndex].CommentList, &cmt)

			// 需要增量导出时，按照updateTime来更新Flag
			if args.IncrementFlag &&
				!*args.AfterTimeStamp &&
				val.UpdatedAt.After(args.TimeStamp) {
				// 发现新数据
				*args.AfterTimeStamp = true
			}
		}
	}

	return nil
}

func aoneWorkerFuncDealMr(args *WorkerFuncArgs) (bool, error) {
	realUrl := args.ParentTaskUrl + strconv.Itoa(args.Page)

	var data []aone.MergeRequest
	requestArgs := RequestArgs{
		ExportPlatformName: AonePlatformName,
		RealUrl:            realUrl,
		Token:              args.Token,
		RetVal:             &data,
	}
	if err := DoGetRequest(requestArgs); err != nil {
		return false, err
	}

	var mrList target.CodeupMergeRequestList

	afterTimeStamp := false
	var UrlDataIds []string
	for _, val := range data {
		if args.IncrementFlag &&
			val.UpdatedAt.After(args.TimeStamp) {
			afterTimeStamp = true
		}
		var mr target.CodeupMergeRequest
		mr.Id = val.ID
		mr.Iid = val.Iid
		mr.State = val.State
		mr.Title = val.Title
		//mr.CreatedAt = timestamppb.New(val.CreatedAt)
		//mr.UpdatedAt = timestamppb.New(val.UpdatedAt)
		mr.CreateAt = val.CreatedAt.Unix()
		mr.UpdateAt = val.UpdatedAt.Unix()
		UrlDataIds = append(UrlDataIds, strconv.FormatInt(val.ID, 10))
		for _, assigner := range val.Assignees {
			// userMap中没有这个user
			// 或者userMap中包含这个user，但是Email是""，同时assigner的email不是""
			// 这个时候使用assigner插入map替换原数值
			oneUser := &target.CodeupUser{
				Id:       assigner.ID,
				ExternId: assigner.ExternUID,
				UserName: assigner.Username,
				Name:     assigner.Name,
				Email:    assigner.Email,
			}
			args.UserMap.Store(assigner.ID, oneUser)
		}

		mrList.MergeRequestList = append(mrList.MergeRequestList, &mr)
	}

	// 第一次会放过，后续就会阻塞住等待任务完成
	if args.SubFuncWg != nil {
		args.SubFuncWg.Wait()
		if args.IncrementFlag &&
			!*args.AfterTimeStamp {
			return false, nil
		}
	}

	if len(data) == 0 {
		return false, nil
	}

	*args.AfterTimeStamp = afterTimeStamp
	args.SubFuncWg = &sync.WaitGroup{}
	*args.StartIndex = *args.EndIndex
	*args.EndIndex = *args.StartIndex + len(data)
	args.UrlDataIds = UrlDataIds

	v, ok := args.Data.(*target.CodeupMergeRequestList)
	if !ok {
		args.Log.Error("mr类型错误")
		return false, errors.New("mr类型错误")
	}
	v.MergeRequestList = append(v.MergeRequestList, mrList.MergeRequestList...)

	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: TaskNameMergeRequest,
		Err:      nil,
		Export:   true,
	}
	recordArgs.Msg = "共获取到" + strconv.Itoa(len(v.MergeRequestList)) + "条MR数据，但还未落盘"
	recordTask(recordArgs)

	return true, nil
}

// aoneDealMergeRequest
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
func aoneDealMergeRequest(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:     args.RepoInfo.RepoId,
		PageSize:   args.PageSize,
		StringTmpl: AoneOpenApiGetAllMergeRequests,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var mrList target.CodeupMergeRequestList
	mrUsers := &sync.Map{}

	stopChan := make(chan error)
	afterTimeStamp := false

	startIndex := 0
	endIndex := 0
	poolStop := false

	workerFuncArgs := WorkerFuncArgs{
		ParentTaskUrl:  baseUrl,
		Page:           1,
		UserMap:        mrUsers,
		Log:            args.Log,
		TaskName:       args.TaskName,
		SubFuncWg:      nil,
		StopChan:       stopChan,
		PoolStop:       &poolStop,
		RepoInfo:       args.RepoInfo,
		Token:          args.Token,
		PageSize:       args.PageSize,
		IncrementFlag:  args.IncrementFlag,
		AfterTimeStamp: &afterTimeStamp,
		TimeStamp:      args.TimeStamp,
		Data:           &mrList,
		StartIndex:     &startIndex,
		EndIndex:       &endIndex,
		FinalDataIndex: 0,
		DataId:         "",
	}

	task := &WorkerTask{
		Parent:     true,
		Args:       workerFuncArgs,
		ParentFunc: aoneWorkerFuncDealMr,
		ChildFunc:  aoneWorkerFuncDealMrCmt,
	}

	GlobalWorkerPool.Submit(task)

	err = <-stopChan
	if err != nil {
		return err
	}

	close(stopChan)

	var userList target.CodeupUserList
	var rangeError error
	rangeError = nil

	mrUsers.Range(func(key, val any) bool {
		v, ok := val.(*target.CodeupUser)
		if !ok {
			rangeError = errors.New("map中数据出现错误")
			return false
		}
		//fmt.Println(v.Name, v.Email, v.UserName)
		userList.UserList = append(userList.UserList, v)
		return true
	})

	if rangeError != nil {
		return rangeError
	}

	// 有数据才写文件
	if len(userList.UserList) > 0 {
		userListArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			ExportPlatformName: AonePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &userList,
		}
		if args.IncrementFlag {
			userListArgs.FileName = BinFileNameMergeRequestUsersIncrement
		} else {
			userListArgs.FileName = BinFileNameMergeRequestUsers
		}

		_, err = WriteProtoData(userListArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(userList.UserList)) + "条用户数据的消息"
	} else {
		recordArgs.Msg = "评论的用户" + LogMsgNoData
	}
	recordTask(recordArgs)

	// 有数据才写文件
	if len(mrList.MergeRequestList) > 0 {
		mrListArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			ExportPlatformName: AonePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &mrList,
		}
		if args.IncrementFlag {
			mrListArgs.FileName = BinFileNameMergeRequestsIncrement
		} else {
			mrListArgs.FileName = BinFileNameMergeRequests
		}
		_, err = WriteProtoData(mrListArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(mrList.MergeRequestList)) + "条Mr数据消息"
	} else {
		recordArgs.Msg = "MergeRequest" + LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// 并发加速根据仓库来读取用户
func aoneMergeUsersByRepo(args MergeArgs) error {
	insert := false

	dataArgs := DataArgs{
		ExportDataDir:      args.BaseConfig.ExportDataDir,
		ExportPlatformName: args.BaseConfig.ExportPlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.Repo,
	}

	// 加载增量导出mr中的User
	var incrementMrUserList target.CodeupUserList
	dataArgs.FileName = BinFileNameMergeRequestUsersIncrement
	dataArgs.Data = &incrementMrUserList
	_, err := ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 文件不存在的错误可以忽略，因为可以理解成没有增量导出，直接merge即可
		// 文件不存在时，返回的len(incrementUserList) == 0，所以逻辑合并
		for _, val := range incrementMrUserList.UserList {
			// 新建对象是为了incrementUserList的垃圾回收
			user := target.CodeupUser{
				Id:       val.Id,
				ExternId: val.ExternId,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载增量导出Issue中的User
	var incrementIssueUserList target.CodeupUserList
	dataArgs.FileName = BinFileNameIssueUsersIncrement
	dataArgs.Data = &incrementIssueUserList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 文件不存在的错误可以忽略，因为可以理解成没有增量导出，直接merge即可
		// 文件不存在时，返回的len(incrementIssueUserList) == 0，所以逻辑合并
		for _, val := range incrementIssueUserList.UserList {
			// 新建对象是为了incrementUserList的垃圾回收
			user := target.CodeupUser{
				Id:       val.Id,
				ExternId: val.ExternId,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载全量导出mr中的user
	var mrUserList target.CodeupUserList
	dataArgs.FileName = BinFileNameMergeRequestUsers
	dataArgs.Data = &mrUserList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range mrUserList.UserList {
			user := target.CodeupUser{
				Id:       val.Id,
				ExternId: val.ExternId,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载comment和mr中的user
	var issueUserList target.CodeupUserList
	dataArgs.FileName = BinFileNameIssueUsers
	dataArgs.Data = &issueUserList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range issueUserList.UserList {
			user := target.CodeupUser{
				Id:       val.Id,
				ExternId: val.ExternId,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载memberList中的user
	var memberList target.CodeupMemberList
	dataArgs.FileName = BinFileNameRepoMembers
	dataArgs.Data = &memberList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range memberList.RepoMember {
			user := target.CodeupUser{
				Id:       val.User.Id,
				ExternId: val.User.ExternId,
				UserName: val.User.UserName,
				Name:     val.User.Name,
				Email:    val.User.Email,
			}
			args.UserMap.Store(val.User.Id, &user)
			insert = true
		}
	} else {
		return err
	}
	if insert && args.Log != nil {
		msg := fmt.Sprintf("仓库-【%s/%s】仓库相关用户参与了去重", args.Repo.RepoGroupName, args.Repo.RepoName)
		args.Log.Info(msg)
	}

	return nil
}

// aone合并用户
func aoneMergeAllUsers(cfg *common.Config) error {
	// 用户去重使用
	userMap := &sync.Map{}
	platformName := GetPlatformName(cfg.BaseConfig.ExportPlatformName)
	// 找到所有已导出仓库，因此调用为导入准备的函数
	repos, err := GetAllRepoInfo(cfg)
	if err != nil {
		return err
	}

	args := MergeArgs{
		UserMap:    userMap,
		BaseConfig: &cfg.BaseConfig,
		Log:        cfg.GlobalLog,
	}

	waitGroup := sync.WaitGroup{}
	var readError error
	readError = nil
	// 分库的每个库的用户
	for idx := range repos {
		waitGroup.Add(1)
		args.Repo = &repos[idx]
		go func(funcArgs MergeArgs) {
			err := aoneMergeUsersByRepo(funcArgs)
			if err != nil {
				readError = err
			}
			waitGroup.Done()
		}(args)
	}

	waitGroup.Wait()
	if readError != nil {
		return readError
	}

	// 需要加载之前刷新过的用户
	var ExportedUsers target.CodeupUserList
	dataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: platformName,
		FileName:           BinFileNameAllUsersProto,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		RepoInfo:           nil,
		Data:               &ExportedUsers,
	}
	_, err = ReadProtoData(dataArgs)
	if err == nil {
		// 文件存在，正常加载
		for _, val := range ExportedUsers.UserList {
			userMap.Store(val.Id, val)
		}
	} else {
		// 文件不存在或是出错，不需要停止，不用处理任何逻辑
	}

	// 不分库的组成员
	var data target.CodeupMemberList
	readDataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: cfg.BaseConfig.ExportPlatformName,
		FileName:           BinFileNameGroupMembers,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &data,
	}
	_, err = ReadProtoData(readDataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range data.RepoMember {
			userMap.Store(val.User.Id, val.User)
		}
	} else if err != nil {
		return err
	}

	// 将所有去重的用户，加入list
	var AllUsers target.CodeupUserList
	var rangeError error
	rangeError = nil
	userMap.Range(func(key, val any) bool {
		v, ok := val.(*target.CodeupUser)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		AllUsers.UserList = append(AllUsers.UserList, v)
		return true
	})
	if rangeError != nil {
		return rangeError
	}

	waitGroup = sync.WaitGroup{}
	var requestError error
	requestError = nil
	for idx := range AllUsers.UserList {
		waitGroup.Add(1)
		user := AllUsers.UserList[idx]
		go func(user *target.CodeupUser) {
			userDetailArgs := aoneUserDetailArgs{
				Token: cfg.BaseConfig.Token,
				User:  user,
			}
			err = aoneGetUserDetail(userDetailArgs)
			if err != nil {
				cfg.GlobalLog.Error(err)
				if strings.Contains(err.Error(), GiteeErrorMsgNotFound404) {
					// 包含时，代表这个错误其实不需要处理，直接跳过即可
				} else {
					requestError = err
				}
			}
			waitGroup.Done()
		}(user)
	}

	waitGroup.Wait()

	dataArgs = DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: platformName,
		FileName:           BinFileNameAllUsersProto,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &AllUsers,
	}

	_, err = WriteProtoData(dataArgs)
	if err != nil {
		return err
	}
	cfg.GlobalLog.Info("User-Merge共落盘", strconv.Itoa(len(AllUsers.UserList)), "条用户信息")

	if requestError != nil {
		return requestError
	}
	return nil
}

// 并发加速根据仓库来读取MergeRequest
func aoneMergeMergeRequestsByRepo(args MergeArgs) error {
	// 最终的MrList
	var finalData target.CodeupMergeRequestList
	// Set去重用
	mrIdSet := make(map[int64]struct{})
	dataArgs := DataArgs{
		ExportDataDir:      args.BaseConfig.ExportDataDir,
		ExportPlatformName: args.BaseConfig.ExportPlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.Repo,
	}

	// 增量数据遍历
	var incrementMr target.CodeupMergeRequestList
	dataArgs.FileName = BinFileNameMergeRequestsIncrement
	dataArgs.Data = &incrementMr
	incrementMrFilePath, err := ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 只要是增量数据，就确定是最新的，直接加入finalData
		for _, val := range incrementMr.MergeRequestList {
			finalData.MergeRequestList = append(finalData.MergeRequestList, val)
			mrIdSet[val.Id] = struct{}{}
		}
	} else if err != nil {
		// 出现其他错误
		return err
	}

	// 全量数据遍历
	var mrList target.CodeupMergeRequestList
	dataArgs.FileName = BinFileNameMergeRequests
	dataArgs.Data = &mrList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 全量文件不存在时，依然执行Merge，只不过无数据
		for _, val := range mrList.MergeRequestList {
			// 增量数据中的已有的，全量数据不再添加
			if _, ok := mrIdSet[val.Id]; !ok {
				finalData.MergeRequestList = append(finalData.MergeRequestList, val)
				mrIdSet[val.Id] = struct{}{}
			}
		}
	} else if err != nil {
		// 出现其他错误
		return err
	}

	// 将finalData覆盖写入全量数据文件中
	if len(finalData.MergeRequestList) > 0 {
		dataArgs.FileName = BinFileNameMergeRequestsMerge
		dataArgs.Data = &finalData
		_, err = WriteProtoData(dataArgs)
		if err != nil {
			return err
		}

		//_ = incrementMrFilePath
		_, err = os.Stat(incrementMrFilePath)
		if err == nil {
			err = os.Remove(incrementMrFilePath)
			if err != nil {
				return err
			}
		}

		if args.Log != nil {
			msg := fmt.Sprintf("仓库-【%s/%s】仓库的mr完成了增量和全量的合并", args.Repo.RepoGroupName, args.Repo.RepoName)
			args.Log.Info(msg)
		}
	}

	return nil
}

// aone合并所有Mr
func aoneMergeAllMergeRequests(cfg *common.Config) error {
	GetPlatformName(cfg.BaseConfig.ExportPlatformName)

	// 加载所有的已导出仓库，因此调用为导入准备的函数
	repos, err := GetAllRepoInfo(cfg)
	if err != nil {
		return err
	}

	args := MergeArgs{
		BaseConfig: &cfg.BaseConfig,
		UserMap:    nil, // 不需要UserMap
		Log:        cfg.GlobalLog,
	}

	waitGroup := sync.WaitGroup{}
	var readError error
	readError = nil
	for idx := range repos {
		waitGroup.Add(1)
		args.Repo = &repos[idx]
		go func(funcArgs MergeArgs) {
			err = aoneMergeMergeRequestsByRepo(funcArgs)
			if err != nil {
				readError = err
			}
			waitGroup.Done()
		}(args)
	}

	waitGroup.Wait()
	if readError != nil {
		return readError
	}

	return nil
}
